﻿using System;
using System.Collections.Generic;
using System.Linq;
using HP.Cachalote.DataModels;
using HP.Cachalote.Repositories.Interfaces;
using HP.Cachalote.Services.Interfaces;
using HP.Cachalote.Services.Validations;

namespace HP.Cachalote.Services
{
    public class TodoLabelService : ServiceBase<TodoLabel>, ITodoLabelService
    {
        private ITodoLabelRepository _todoLabelRepository;

        public TodoLabelService(IRepositoryFactory repositoryFactory) : base(repositoryFactory, new TodoLabelValidator()) { }

        public override void Delete(TodoLabel entity)
        {
            using (RepositoryFactory)
            {
                RepositoryFactory.OpenSessionWithTransaction();
                _todoLabelRepository = RepositoryFactory.CreateTodoLabelRepository();
                ResetOrderIndexWithRemovedLabel(entity);
                _todoLabelRepository.Delete(entity);
            }
        }

        /// <summary>
        /// Insert new TodoLabel based on the OrderIndex. Also will reset the others' OrderIndex.
        /// </summary>
        /// <param name="todoLabel">The new inserted TodoLabel with new OrderIndex.</param>
        /// <returns>The id of the new added TodoLabel.</returns>
        public object Insert(TodoLabel todoLabel)
        {
            EntityValidator.Validate(todoLabel);

            if (EntityValidator.IsValid)
            {
                using (RepositoryFactory)
                {
                    RepositoryFactory.OpenSessionWithTransaction();
                    _todoLabelRepository = RepositoryFactory.CreateTodoLabelRepository();
                    ResetOrderIndexWithNewAddedLabel(todoLabel);

                    return _todoLabelRepository.Add(todoLabel);
                }
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Reset the others' OrderIndex based on the new added TodoLabel.
        /// </summary>
        /// <param name="todoLabel">The specified TodoLabel with new OrderIndex.</param>
        private void ResetOrderIndexWithNewAddedLabel(TodoLabel newAddedTodoLabel)
        {
            //If use SQL Statement then one can make the same effect.
            //UPDATE 
            //    Labels 
            //SET 
            //    OrderIndex = OrderIndex + 1 
            //WHERE 
            //    ParentID = ? AND 
            //    OrderIndex >= ?

            var todoLabels = from label in _todoLabelRepository.Filter(l => l.Parent == newAddedTodoLabel.Parent &&
                l.OrderIndex >= newAddedTodoLabel.OrderIndex)
                             select label;

            foreach (var item in todoLabels)
            {
                item.OrderIndex += 1;
                _todoLabelRepository.Update(item);
            }
        }

        /// <summary>
        /// Reset the others' OrderIndex based on the removed TodoLabel.
        /// </summary>
        /// <param name="todoLabel">The specified TodoLabel with new OrderIndex.</param>
        private void ResetOrderIndexWithRemovedLabel(TodoLabel removedTodoLabel)
        {
            //If use SQL Statement then one can make the same effect.
            //UPDATE 
            //    Labels 
            //SET 
            //    OrderIndex = OrderIndex - 1 
            //WHERE 
            //    ParentID = ? AND 
            //    OrderIndex > ?

            var todoLabels = from label in _todoLabelRepository.Filter(l => l.Parent == removedTodoLabel.Parent &&
                l.OrderIndex > removedTodoLabel.OrderIndex)
                             select label;

            foreach (var item in todoLabels)
            {
                item.OrderIndex -= 1;
                _todoLabelRepository.Update(item);
            }
        }

        #region Load all TodoLabels instead of load them by demond. If the the data amount is low then this is not a big deal.
        /// <summary>
        /// Get a label list with Hierarchical struct. It means load entire tree at one time.
        /// </summary>
        /// <returns>The label list.</returns>
        //public IList<TodoLabel> GetHierarchicalLabelList()
        //{
        //    using (RepositoryFactory)
        //    {
        //        RepositoryFactory.OpenSession();
        //        var result = RepositoryFactory.CreateLabelRepository().Filter(label => label.Parent == null).ToList();

        //        foreach (var label in result)
        //        {
        //            LoadChildNotes(label);
        //        }

        //        return result;
        //    }
        //}

        /// <summary>
        /// Load the sub-labels for a provided label
        /// </summary>
        /// <param name="label">The parent label.</param>
        //private void LoadChildNotes(TodoLabel label)
        //{
        //    // When you trying to get the SubLabels then NHibernate will hit the data source and retrieve the data for you.
        //    if (label.SubLabels.Count > 0)
        //    {
        //        foreach (var subLabel in label.SubLabels)
        //        {
        //            LoadChildNotes(subLabel);
        //        }
        //    }
        //}
        #endregion
    }
}
